linux网络编程(三)

您所在的位置:网站首页 linux bind unbind linux网络编程(三)

linux网络编程(三)

2023-04-08 10:13| 来源: 网络整理| 查看: 265

今天我们来看bind()函数,函数原型如下:

#include int bind(int sockfd, const struct sockaddr *address, socklen_t address_len);

该函数的功能

给socket绑定一个地址,这样client对这个地址的相应收发数据就能和socket相关联

服务端: 必须要调用bind进行绑定

客户端: 非必须调用,如不调用,则系统自动分配一个端口和本地地址来进行和socket绑定

函数参数

sockfd:socket文件描述符

address:构造一个ip+端口的地址,结构体类型为structsockaddr

address_len:地址参数的长度sizeof(address)

返回值:成功返回0,失败返回-1, 并且设置errno

这里address参数值的一说:

struct sockaddr { sa_family_t sa_family; /* address family, AF_xxx */ char sa_data[14]; /* 14 bytes of protocol address */ };

这是早期的协议地址类型,后面出现了IPV4、IPV6,又定义了新的sockaddr

//ipv4 struct sockaddr_in { __kernel_sa_family_t sin_family; /* Address family */ __be16 sin_port; /* Port number */ struct in_addr sin_addr; /* Internet address */ /* Pad to size of `struct sockaddr'. */ unsigned char __pad[__SOCK_SIZE__ - sizeof(short int) - sizeof(unsigned short int) - sizeof(struct in_addr)]; }; /* Internet address. */ struct in_addr { __be32 s_addr; }; //ipv6 struct sockaddr_in6 { unsigned short int sin6_family; /* AF_INET6 */ __be16 sin6_port; /* Transport layer port # */ __be32 sin6_flowinfo; /* IPv6 flow information */ struct in6_addr sin6_addr; /* IPv6 address */ __u32 sin6_scope_id; /* scope id (new in RFC2553) */ }; //unix addr #define UNIX_PATH_MAX 108 struct sockaddr_un { __kernel_sa_family_t sun_family; /* AF_UNIX */ char sun_path[UNIX_PATH_MAX]; };

比如绑定一个ipv4地址:

#include #include #include #include #include #include int main(int argc, char **argv) { int fd = socket(AF_INET, SOCK_STREAM, 0); printf("fd:%d\n", fd); struct sockaddr_in servaddr; bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(8080); int ret = bind(fd, (struct sockaddr*)&servaddr, sizeof(servaddr)); if (ret = 0) { // SELInux相关的处理 err = security_socket_bind(sock, (struct sockaddr *)&address, addrlen); if (!err) // 如果是SOCK_STREAM的话调用的是inet_bind() err = sock->ops->bind(sock, (struct sockaddr *) &address, addrlen); } fput_light(sock->file, fput_needed); } return err; }

通过fd找到socket实例

static struct socket *sockfd_lookup_light(int fd, int *err, int *fput_needed) { // 从当前task_struct->files中找到fd实例 struct fd f = fdget(fd); struct socket *sock; *err = -EBADF; if (f.file) { // 通过file找到socket实例 sock = sock_from_file(f.file, err); if (likely(sock)) { *fput_needed = f.flags & FDPUT_FPUT; return sock; } fdput(f); } return NULL; }

bind的实现func:

const struct proto_ops inet_stream_ops = { .family = PF_INET, .owner = THIS_MODULE, .release = inet_release, .bind = inet_bind, // 这就是bind的实现func .connect = inet_stream_connect, .socketpair = sock_no_socketpair, .accept = inet_accept, .getname = inet_getname, ...... };

该func内部其实就是对sock->sk进行一些赋值

int inet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) { struct sock *sk = sock->sk; int err; /* If the socket has its own bind function then use it. (RAW) */ if (sk->sk_prot->bind) { return sk->sk_prot->bind(sk, uaddr, addr_len); } if (addr_len sk_net会被赋值 unsigned short snum; // 端口 int chk_addr_ret; // 地址类型: 单播 多播 广播 u32 tb_id = RT_TABLE_LOCAL; int err; // 非inet协议簇 if (addr->sin_family != AF_INET) { /* Compatibility games : accept AF_UNSPEC (mapped to AF_INET) * only if s_addr is INADDR_ANY. */ err = -EAFNOSUPPORT; if (addr->sin_family != AF_UNSPEC || addr->sin_addr.s_addr != htonl(INADDR_ANY)) goto out; } // 查找地址类型 tb_id = l3mdev_fib_table_by_index(net, sk->sk_bound_dev_if) ? : tb_id; chk_addr_ret = inet_addr_type_table(net, addr->sin_addr.s_addr, tb_id); /* Not specified by any standard per-se, however it breaks too * many applications when removed. It is unfortunate since * allowing applications to make a non-local bind solves * several problems with systems using dynamic addressing. * (ie. your servers still start up even if your ISDN link * is temporarily down) */ err = -EADDRNOTAVAIL; if (!inet_can_nonlocal_bind(net, inet) && addr->sin_addr.s_addr != htonl(INADDR_ANY) && chk_addr_ret != RTN_LOCAL && chk_addr_ret != RTN_MULTICAST && chk_addr_ret != RTN_BROADCAST) goto out; snum = ntohs(addr->sin_port); // 绑定的端口进行字节序转换 err = -EACCES; if (snum && snum user_ns, CAP_NET_BIND_SERVICE)) goto out; /* We keep a pair of addresses. rcv_saddr is the one * used by hash lookups, and saddr is used for transmit. * * In the BSD API these are the same except where it * would be illegal to use them (multicast/broadcast) in * which case the sending device address is used. */ if (with_lock) lock_sock(sk); // 如果状态不是初始的TCP_CLOSE则报错 /* Check these errors (active socket, double bind). */ err = -EINVAL; if (sk->sk_state != TCP_CLOSE || inet->inet_num) goto out_release_sock; inet->inet_rcv_saddr = inet->inet_saddr = addr->sin_addr.s_addr; if (chk_addr_ret == RTN_MULTICAST || chk_addr_ret == RTN_BROADCAST) inet->inet_saddr = 0; /* Use device */ // 如果没有传端口,则随机选择一个端口,注意:0~1024需要特权 /* Make sure we are allowed to bind here. */ if (snum || !(inet->bind_address_no_port || force_bind_address_no_port)) { if (sk->sk_prot->get_port(sk, snum)) { inet->inet_saddr = inet->inet_rcv_saddr = 0; err = -EADDRINUSE; goto out_release_sock; } err = BPF_CGROUP_RUN_PROG_INET4_POST_BIND(sk); if (err) { inet->inet_saddr = inet->inet_rcv_saddr = 0; goto out_release_sock; } } if (inet->inet_rcv_saddr) sk->sk_userlocks |= SOCK_BINDADDR_LOCK; if (snum) sk->sk_userlocks |= SOCK_BINDPORT_LOCK; inet->inet_sport = htons(inet->inet_num); inet->inet_daddr = 0; inet->inet_dport = 0; sk_dst_reset(sk); err = 0; out_release_sock: if (with_lock) release_sock(sk); out: return err; }

总结

bind函数内部做的事其实很单一,主要如下:

1.通过fd找到对应的socket实例

2.进行(地址+端口)的参数校验

3.对socket实例中的成员赋值



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3